#include "DataManager.h"
#include "../fileio/FileManager.h"
#include "Data.h"
#include "../const/const.h"
#include "../datamanager/DataManager.h"
#include "../indexmanager/IndexManager.h"
#include "../utils/pagedef.h"
#include <string>
#include <vector>
#include <cstring>
#include <utility>
#include <iostream>
#include <cstdlib>
using namespace std;

bool strLikeMatch(string str, string matchStr){
    if(matchStr.size() == 0) return str.size() == 0;
    else if(matchStr[0] == '_' && str.size() > 0)
        return strLikeMatch(str.substr(1), matchStr.substr(1));
    else if(matchStr[0] == '%'){
        for(int i = 0; i <= str.size(); ++i){
            if(strLikeMatch(str.substr(i), matchStr.substr(1)))
                return true;
        }
        return false;
    }
    else if(str.size() == 0) return false;
    else if(matchStr[0] == str[0]) return strLikeMatch(str.substr(1), matchStr.substr(1));
    else return false;
}

bool DataManager::judgeCondition(std::vector<std::string>& singleData, search_condition& condition, int& nullInf){
    int f = condition.first;
    int col = f % MAX_COLUMN_NUM;
    f /= MAX_COLUMN_NUM;
    int type = f % WHERE_TYPE_NUM;
    if(type == WHERE_NULL){
        return (nullInf >> col) & 1;
    }
    else if(type == WHERE_NOT_NULL){
        return !((nullInf >> col) & 1);
    }
    else if(type == WHERE_OP_VAL){
        if((nullInf >> col) & 1) return false;
        f /= WHERE_TYPE_NUM;
        int op = f % WHERE_OP_NUM;
        if(op == WHERE_OP_EQUAL)
            return (condition.second.size() <= singleData[col].length()) && (singleData[col] == Data::fillStr(condition.second, singleData[col].length()));
        else if(op == WHERE_OP_NOT_EQUAL)
            return (condition.second.size() > singleData[col].length()) || (singleData[col] != Data::fillStr(condition.second, singleData[col].length()));
        else if(op == WHERE_OP_LTE)
            return atoi(singleData[col].c_str()) <= atoi(condition.second.c_str());
        else if(op == WHERE_OP_GTE)
            return atoi(singleData[col].c_str()) >= atoi(condition.second.c_str());
        else if(op == WHERE_OP_LESS)
            return atoi(singleData[col].c_str()) < atoi(condition.second.c_str());
        else if(op == WHERE_OP_MORE)
            return atoi(singleData[col].c_str()) > atoi(condition.second.c_str());
        else if(op == WHERE_OP_LIKE)
            return strLikeMatch(singleData[col].c_str(), condition.second.c_str());
    }
    else if(type == WHERE_OP_COL){
        if((nullInf >> col) & 1) return false;
        f /= WHERE_TYPE_NUM;
        int op = f % WHERE_OP_NUM;
        int col2 = atoi(condition.second.c_str());
        string c2 = singleData[col2].c_str();
        if(op == WHERE_OP_EQUAL)
            return (c2.length() <= singleData[col].length()) && (singleData[col] == Data::fillStr(c2, singleData[col].length()));
        else if(op == WHERE_OP_NOT_EQUAL)
            return (c2.length() > singleData[col].length()) || singleData[col] != Data::fillStr(c2, singleData[col].length());
        else if(op == WHERE_OP_LTE)
            return atoi(singleData[col].c_str()) <= atoi(singleData[col2].c_str());
        else if(op == WHERE_OP_GTE)
            return atoi(singleData[col].c_str()) >= atoi(singleData[col2].c_str());
        else if(op == WHERE_OP_LESS)
            return atoi(singleData[col].c_str()) < atoi(singleData[col2].c_str());
        else if(op == WHERE_OP_MORE)
            return atoi(singleData[col].c_str()) > atoi(singleData[col2].c_str());
        else if(op == WHERE_OP_LIKE)
            return strLikeMatch(singleData[col].c_str(), singleData[col2].c_str());
    }
    else{
        std::cout << "Error : Wrong type of search condition." << std::endl;
        return false;
    }
}

void DataManager::initDatabase(){
    int index;
    BufType buf = bpm->getPage(fileID, FIRST_SYSOBJ_PAGE, index);
    bpm->markDirty(index);
    buf[PAGE_HEAD_INFO] = 0;
    buf[PAGE_HEAD_NEXT_PAGE] = 1 << 30;
    buf[PAGE_HEAD_DATA_NUM] = 0;
    for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i)
        buf[i] = 0;
    buf = bpm->getPage(fileID, FIRST_SYSCOLUMN_PAGE, index);
    bpm->markDirty(index);
    buf[PAGE_HEAD_INFO] = 0;
    buf[PAGE_HEAD_NEXT_PAGE] = 1 << 30;
    buf[PAGE_HEAD_DATA_NUM] = 0;
    for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i)
        buf[i] = 0;
    buf = bpm->getPage(fileID, FIRST_SYSINDEX_PAGE, index);
    bpm->markDirty(index);
    buf[PAGE_HEAD_INFO] = 0;
    buf[PAGE_HEAD_NEXT_PAGE] = 1 << 30;
    buf[PAGE_HEAD_DATA_NUM] = 0;
    for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i)
        buf[i] = 0;
    buf = bpm->getPage(fileID, FIRST_DATABASE_PAGE, index);
    bpm->markDirty(index);
    buf[DATABASE_PAGE_NEXT_EMPTY_PAGE] = FIRST_USER_PAGE;
}


int DataManager::insertData(Data &data, std::string table){
    if(table != SYS_OBJ && table != SYS_INDEX && table != SYS_COLUMN){
        vector<search_condition> con;
        con.push_back(search_condition(N_TABLE_NAME,table));
        search_result res;
        searchData(con, SYS_OBJ, res);
        if(res.size() == 0){
            cout << "Error : Table not found!" << endl;
            return 0;
        }
        string RID = res[0][N_RID];
        con.clear();
        con.push_back(search_condition(N_INDEX_ID,RID));
        searchData(con, SYS_INDEX, res);
        if(res.size() == 0){
            cout << "Error : Table in system is ruined." << endl;
            return 0;
        }
        int primary_key = Data::intUnzip(&(res[0][N_TABLE_KEY][0]),MAX_TABLE_KEY);
        if(primary_key != MAX_COLUMN_NUM){
            con.clear();
            con.push_back(search_condition(primary_key, data.getColumn(primary_key)));
            searchData(con, table, res);
            if(res.size() > 0){
                cout << "Error : Repeated primary key is not allowed." << endl;
                return 0;
            }
        }
    }
    int table_page, index, r_id, has_index, index_page;
    getTableInfo(table, data, table_page, has_index);
    index_page = table_page + 1;
    BufType buf;
    while(true){
        buf = bpm->getPage(fileID, table_page, index);
        if((buf[PAGE_HEAD_INFO] & 1) == 0){
            bpm->markDirty(index);
            break;
        }
        int old_page = table_page;
        table_page = buf[PAGE_HEAD_NEXT_PAGE];
        if(buf[PAGE_HEAD_NEXT_PAGE] == 1 << 30){
            buf = bpm->getPage(fileID, FIRST_DATABASE_PAGE, index);
            bpm->markDirty(index);
            table_page = buf[DATABASE_PAGE_NEXT_EMPTY_PAGE];
            ++buf[DATABASE_PAGE_NEXT_EMPTY_PAGE];
            buf = bpm->getPage(fileID, old_page, index);
            bpm->markDirty(index);
            buf[PAGE_HEAD_NEXT_PAGE] = table_page;
            buf = bpm->getPage(fileID, table_page, index);
            bpm->markDirty(index);
            buf[PAGE_HEAD_INFO] = 0;
            buf[PAGE_HEAD_NEXT_PAGE] = 1 << 30;
            buf[PAGE_HEAD_DATA_NUM] = 0;
            for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i)
                buf[i] = 0;
            break;
        }
    }
    //下面循环访问位数组并提取出位数组中的空闲条目
    bool flag = false;
    int offset;
    for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i){
        int j;
        for(j = 0; j < 32; ++j){
            if(((buf[i]>>j)&1) == 0){
                flag = true;
                buf[i] |= (1 << j);
                break;
            }
        }
        if(flag){
            offset = (i-PAGE_HEAD_IN_USE) * 32 + j;
            break;
        }
    }
    r_id = table_page * MAX_DATA_NUM + offset;
    data.setRID(r_id);
    ++ buf[PAGE_HEAD_DATA_NUM];
    string toWrite = data.toString();
    buf[PAGE_HEAD_DATA_LEN] = toWrite.length();
    char* writeBuf = (char*) buf;
    memcpy(writeBuf + PAGE_HEAD_LEN*4 + offset*buf[PAGE_HEAD_DATA_LEN], toWrite.c_str(), toWrite.length());
    flag = false;
    for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i){
        int j;
        for(j = 0; j < 32; ++j){
            if(((buf[i]>>j)&1) == 0){
                flag = true;
                break;
            }
        }
        if(flag){
            offset = (i-PAGE_HEAD_IN_USE) * 32 + j;
            break;
        }
    }
    if(PAGE_HEAD_LEN*4 + offset*buf[PAGE_HEAD_DATA_LEN] + toWrite.length()*2 > PAGE_SIZE)
        buf[PAGE_HEAD_INFO] |= 1;
    if(has_index != 0){
        int column_num = data.getColumnNum();
        for(int i = 0; i <= column_num; ++i){
            if(((has_index>>i)&1) == 1){
                int hash = IndexManager::indexHash(data.getColumn(i));
                buf = bpm->getPage(fileID, index_page, index);
                bpm->markDirty(index);
                IndexManager im;
                im.setDataManager(this);
                im.setCurrentPosition(position(buf[INDEX_PAGE_POS + i*2], buf[INDEX_PAGE_POS + i*2 + 1]));
                im.addNode(hash, r_id, buf[INDEX_PAGE_FIRST_PAGE+i]);
                buf = bpm->getPage(fileID, index_page, index);
                bpm->markDirty(index);
                buf[INDEX_PAGE_POS + i*2] = im.getCurrentPosition().first;
                buf[INDEX_PAGE_POS + i*2 + 1] = im.getCurrentPosition().second;
            }
        }
    }
    return r_id;
}

void DataManager::searchData(vector<search_condition> &condition, std::string table, search_result& searchResult){
    searchResult.clear();
    std::vector<std::string> singleData;
    Data readFunc;
    int table_page, index, has_index;
    getTableInfo(table, readFunc, table_page, has_index);
    bool find_index = false;
    for(int i = 0; i < condition.size(); ++i){
        if(condition[i].first >= MAX_COLUMN_NUM) continue;
        if(((has_index>>condition[i].first)&1) == 1){
            int hash = IndexManager::indexHash(condition[i].second);
            BufType buf = bpm->getPage(fileID, table_page+1, index);
            IndexManager im;
            im.setDataManager(this);
            im.setCurrentPosition(position(buf[INDEX_PAGE_POS + condition[i].first*2], buf[INDEX_PAGE_POS + condition[i].first*2 + 1]));
            std::vector<int> valueList = im.searchKey(hash, buf[INDEX_PAGE_FIRST_PAGE+condition[i].first]);
            int len;
            for(int j = 0; j < valueList.size(); ++j){
                buf = bpm->getPage(fileID, valueList[j]/MAX_DATA_NUM, index);
                char* readbuf = (char*) (buf + PAGE_HEAD_LEN);
                singleData.clear();
                int nullInf;
                readFunc.readData(readbuf + (valueList[j] % MAX_DATA_NUM)*buf[PAGE_HEAD_DATA_LEN], singleData, len, nullInf);
                bool flag = true;
                for(int t = 0; t < condition.size(); ++t)
                {
                    if(!judgeCondition(singleData, condition[t], nullInf)){
                        flag = false;
                        break;
                    }
                }
                if(flag)
                    searchResult.push_back(singleData);
            }
            find_index = true;
            break;
        }
    }
    if(!find_index){
        while(table_page != 1 << 30){
            int offset = 0, len;
            BufType buf = bpm->getPage(fileID, table_page, index);
            char* readbuf = (char*) (buf + PAGE_HEAD_LEN);
            int covered_data = 0;
            for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i){
                for(int j = 0; j < 32; ++j){
                    if(((buf[i]>>j)&1) == 1){
                        singleData.clear();
                        offset = (i-PAGE_HEAD_IN_USE) * 32 + j;
                        int nullInf;
                        readFunc.readData(readbuf + offset*buf[PAGE_HEAD_DATA_LEN], singleData, len, nullInf);
                        bool flag = true;
                        for(int t = 0; t < condition.size(); ++t)
                        {
                            if(!judgeCondition(singleData, condition[t], nullInf)){
                                flag = false;
                                break;
                            }
                        }
                        if(flag)
                            searchResult.push_back(singleData);
                        ++covered_data;
                    }
                    if(covered_data >= buf[PAGE_HEAD_DATA_NUM])
                        break;
                }
                if(covered_data >= buf[PAGE_HEAD_DATA_NUM])
                    break;
            }
            table_page = buf[PAGE_HEAD_NEXT_PAGE];
        }
    }
}

void DataManager::deleteData(vector<search_condition> &condition, std::string table){
    std::vector<std::string> singleData;
    Data readFunc;
    int table_page, index, has_index;
    getTableInfo(table, readFunc, table_page, has_index);
    int origin_tb_page = table_page;
    bool find_index = false;
    for(int i = 0; i < condition.size(); ++i){
        if(condition[i].first >= MAX_COLUMN_NUM) continue;
        if(((has_index>>condition[i].first)&1) == 1){
            int hash = IndexManager::indexHash(condition[i].second);
            BufType buf = bpm->getPage(fileID, table_page+1, index);
            IndexManager im;
            im.setDataManager(this);
            im.setCurrentPosition(position(buf[INDEX_PAGE_POS + condition[i].first*2], buf[INDEX_PAGE_POS + condition[i].first*2 + 1]));
            std::vector<int> valueList = im.searchKey(hash, buf[INDEX_PAGE_FIRST_PAGE+condition[i].first]);
            int len, offset;
            for(int j = 0; j < valueList.size(); ++j){
                buf = bpm->getPage(fileID, valueList[j]/MAX_DATA_NUM, index);
                char* readbuf = (char*) (buf + PAGE_HEAD_LEN);
                singleData.clear();
                offset = valueList[j] % MAX_DATA_NUM;
                int nullInf;
                readFunc.readData(readbuf + offset*buf[PAGE_HEAD_DATA_LEN], singleData, len, nullInf);
                bool flag = true;
                for(int t = 0; t < condition.size(); ++t)
                {
                    if(!judgeCondition(singleData, condition[t], nullInf)){
                        flag = false;
                        break;
                    }
                }
                if(flag){
                    buf[PAGE_HEAD_IN_USE + offset/32] ^= (1 << (offset%32));
                    if((buf[PAGE_HEAD_INFO]&1) == 1)
                        buf[PAGE_HEAD_INFO] ^= 1;
                    bpm->markDirty(index);
                    -- buf[PAGE_HEAD_DATA_NUM];
                    for(int t = 0; t < singleData.size(); ++t){
                        if(((has_index>>t)&1) == 1){
                            int hash = IndexManager::indexHash(singleData[t]);
                            buf = bpm->getPage(fileID, table_page+1, index);
                            bpm->markDirty(index);
                            IndexManager im;
                            im.setDataManager(this);
                            im.setCurrentPosition(position(buf[INDEX_PAGE_POS + t*2], buf[INDEX_PAGE_POS + t*2 + 1]));
                            im.deleteNode(hash, valueList[j], buf[INDEX_PAGE_FIRST_PAGE+t]);
                            buf[INDEX_PAGE_POS + t*2] = im.getCurrentPosition().first;
                            buf[INDEX_PAGE_POS + t*2 + 1] = im.getCurrentPosition().second;
                        }
                    }
                }
            }
            find_index = true;
            break;
        }
    }
    if(!find_index){
        while(table_page != 1 << 30){
            int offset = 0, len;
            BufType buf = bpm->getPage(fileID, table_page, index);
            char* readbuf = (char*) (buf + PAGE_HEAD_LEN);
            int covered_data = 0;
            for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i){
                for(int j = 0; j < 32; ++j){
                    if(((buf[i]>>j)&1) == 1){
                        singleData.clear();
                        offset = (i-PAGE_HEAD_IN_USE) * 32 + j;
                        int nullInf;
                        readFunc.readData(readbuf + offset*buf[PAGE_HEAD_DATA_LEN], singleData, len, nullInf);
                        bool flag = true;
                        for(int t = 0; t < condition.size(); ++t)
                        {
                            if(!judgeCondition(singleData, condition[t], nullInf)){
                                flag = false;
                                break;
                            }
                        }
                        if(flag){
                            buf[i] ^= (1 << j);
                            if((buf[PAGE_HEAD_INFO]&1) == 1)
                                buf[PAGE_HEAD_INFO] ^= 1;
                            bpm->markDirty(index);
                            -- buf[PAGE_HEAD_DATA_NUM];
                            for(int t = 0; t < singleData.size(); ++t){
                                if(((has_index>>t)&1) == 1){
                                    int hash = IndexManager::indexHash(singleData[t]);
                                    int tindex;
                                    BufType tbuf = bpm->getPage(fileID, origin_tb_page+1, tindex);
                                    bpm->markDirty(tindex);
                                    IndexManager im;
                                    im.setDataManager(this);
                                    im.setCurrentPosition(position(tbuf[INDEX_PAGE_POS + t*2], tbuf[INDEX_PAGE_POS + t*2 + 1]));
                                    int value = table_page * MAX_DATA_NUM + offset;
                                    im.deleteNode(hash, value, tbuf[INDEX_PAGE_FIRST_PAGE+t]);
                                    tbuf[INDEX_PAGE_POS + t*2] = im.getCurrentPosition().first;
                                    tbuf[INDEX_PAGE_POS + t*2 + 1] = im.getCurrentPosition().second;
                                }
                            }
                        }
                        else
                            ++covered_data;
                    }
                    if(covered_data >= buf[PAGE_HEAD_DATA_NUM])
                        break;
                }
                if(covered_data >= buf[PAGE_HEAD_DATA_NUM])
                    break;
            }
            table_page = buf[PAGE_HEAD_NEXT_PAGE];
        }
    }
}

void DataManager::updateData(vector<search_condition> &condition, std::string table, vector<replace_pair>& _newPair){
    std::vector<std::string> singleData, oldData;
    std::vector<replace_pair> newPair;
    Data readFunc;
    int table_page, index, has_index;
    getTableInfo(table, readFunc, table_page, has_index);
    int origin_tb_page = table_page;
    bool find_index = false;
    for(int i = 0; i < condition.size(); ++i){
        if(condition[i].first >= MAX_COLUMN_NUM) continue;
        if(((has_index>>condition[i].first)&1) == 1){
            int hash = IndexManager::indexHash(condition[i].second);
            BufType buf = bpm->getPage(fileID, table_page+1, index);
            IndexManager im;
            im.setDataManager(this);
            im.setCurrentPosition(position(buf[INDEX_PAGE_POS + condition[i].first*2], buf[INDEX_PAGE_POS + condition[i].first*2 + 1]));
            std::vector<int> valueList = im.searchKey(hash, buf[INDEX_PAGE_FIRST_PAGE+condition[i].first]);
            int len, offset;
            for(int j = 0; j < valueList.size(); ++j){
                buf = bpm->getPage(fileID, valueList[j]/MAX_DATA_NUM, index);
                char* readbuf = (char*) (buf + PAGE_HEAD_LEN);
                singleData.clear();
                offset = valueList[j] % MAX_DATA_NUM;
                int nullInf;
                readFunc.readData(readbuf + offset*buf[PAGE_HEAD_DATA_LEN], singleData, len, nullInf);
                bool flag = true;
                for(int t = 0; t < condition.size(); ++t)
                {
                    if(!judgeCondition(singleData, condition[t], nullInf)){
                        flag = false;
                        break;
                    }
                }
                if(flag){
                    newPair.clear();
                    oldData.clear();
                    for(int k = 0; k < singleData.size(); ++k) oldData.push_back(singleData[k]);
                    for(int k = 0; k < _newPair.size(); ++k) newPair.push_back(_newPair[k]);
                    bpm->markDirty(index);
                    vector<int> lenList;
                    for(int k = 1; k < singleData.size(); ++k)
                        lenList.push_back(singleData[k].length());
                    Data newData;
                    newData.setRID(singleData[0]);
                    for(int k = 0; k < newPair.size(); ++k){
                        if(newPair[k].first >= MAX_COLUMN_NUM){
                            newPair[k].first -= MAX_COLUMN_NUM;
                            singleData[newPair[k].first] = "";
                            nullInf = nullInf | (1 << newPair[k].first);
                        }
                        else{
                            singleData[newPair[k].first] = newPair[k].second;
                            if((nullInf >> newPair[k].first) & 1)
                                nullInf = nullInf ^ (1 << newPair[k].first);
                        }
                    }
                    for(int k = 1; k < singleData.size(); ++k){
                        if((nullInf >> k) & 1) newData.insertNull();
                        else newData.insertValue(singleData[k]);
                    }
                    newData.setLength(lenList);
                    string toWrite = newData.toString();
                    memcpy(readbuf + offset*buf[PAGE_HEAD_DATA_LEN], toWrite.c_str(), toWrite.length());
                    for(int t = 0; t < newPair.size(); ++t){
                        if(((has_index>>newPair[t].first)&1) == 1){
                            int hash = IndexManager::indexHash(oldData[newPair[t].first]);
                            int newHash = IndexManager::indexHash(newPair[t].second);
                            buf = bpm->getPage(fileID, origin_tb_page+1, index);
                            bpm->markDirty(index);
                            IndexManager im;
                            im.setDataManager(this);
                            im.setCurrentPosition(position(buf[INDEX_PAGE_POS + newPair[t].first*2], buf[INDEX_PAGE_POS + newPair[t].first*2 + 1]));
                            im.deleteNode(hash, valueList[j], buf[INDEX_PAGE_FIRST_PAGE+newPair[t].first]);
                            im.addNode(newHash, valueList[j], buf[INDEX_PAGE_FIRST_PAGE+newPair[t].first]);
                            buf[INDEX_PAGE_POS + newPair[t].first*2] = im.getCurrentPosition().first;
                            buf[INDEX_PAGE_POS + newPair[t].first*2 + 1] = im.getCurrentPosition().second;
                        }
                    }
                }
            }
            find_index = true;
            break;
        }
    }
    if(!find_index){
        while(table_page != 1 << 30){
            int offset = 0, len;
            BufType buf = bpm->getPage(fileID, table_page, index);
            char* readbuf = (char*) (buf + PAGE_HEAD_LEN);
            int covered_data = 0;
            for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i){
                for(int j = 0; j < 32; ++j){
                    if(((buf[i]>>j)&1) == 1){
                        singleData.clear();
                        offset = (i-PAGE_HEAD_IN_USE) * 32 + j;
                        int nullInf;
                        readFunc.readData(readbuf + offset*buf[PAGE_HEAD_DATA_LEN], singleData, len, nullInf);
                        bool flag = true;
                        for(int t = 0; t < condition.size(); ++t)
                        {
                            if(!judgeCondition(singleData, condition[t], nullInf)){
                                flag = false;
                                break;
                            }
                        }
                        if(flag){
                            newPair.clear();
                            oldData.clear();
                            for(int k = 0; k < singleData.size(); ++k) oldData.push_back(singleData[k]);
                            for(int k = 0; k < _newPair.size(); ++k) newPair.push_back(_newPair[k]);
                            bpm->markDirty(index);
                            vector<int> lenList;
                            for(int k = 1; k < singleData.size(); ++k)
                                lenList.push_back(singleData[k].length());
                            Data newData;
                            newData.setRID(singleData[0]);
                            for(int k = 0; k < newPair.size(); ++k){
                                if(newPair[k].first >= MAX_COLUMN_NUM){
                                    newPair[k].first -= MAX_COLUMN_NUM;
                                    singleData[newPair[k].first] = "";
                                    nullInf = nullInf | (1 << newPair[k].first);
                                }
                                else{
                                    singleData[newPair[k].first] = newPair[k].second;
                                    if((nullInf >> newPair[k].first) & 1)
                                        nullInf = nullInf ^ (1 << newPair[k].first);
                                }
                            }
                            for(int k = 1; k < singleData.size(); ++k){
                                if((nullInf >> k) & 1) newData.insertNull();
                                else newData.insertValue(singleData[k]);
                            }
                            newData.setLength(lenList);
                            string toWrite = newData.toString();
                            memcpy(readbuf + offset*buf[PAGE_HEAD_DATA_LEN], toWrite.c_str(), toWrite.length());
                            for(int t = 0; t < newPair.size(); ++t){
                                if(((has_index>>newPair[t].first)&1) == 1){
                                    int hash = IndexManager::indexHash(oldData[newPair[t].first]);
                                    int newHash = IndexManager::indexHash(newPair[t].second);
                                    int tindex;
                                    BufType tbuf = bpm->getPage(fileID, table_page+1, tindex);
                                    bpm->markDirty(tindex);
                                    IndexManager im;
                                    im.setDataManager(this);
                                    im.setCurrentPosition(position(tbuf[INDEX_PAGE_POS + newPair[t].first*2], tbuf[INDEX_PAGE_POS + newPair[t].first*2 + 1]));
                                    int value = table_page * MAX_DATA_NUM + offset;
                                    im.deleteNode(hash, value, tbuf[INDEX_PAGE_FIRST_PAGE+newPair[t].first]);
                                    im.addNode(newHash, value, tbuf[INDEX_PAGE_FIRST_PAGE+newPair[t].first]);
                                    tbuf[INDEX_PAGE_POS + newPair[t].first*2] = im.getCurrentPosition().first;
                                    tbuf[INDEX_PAGE_POS + newPair[t].first*2 + 1] = im.getCurrentPosition().second;
                                }
                            }
                        }
                        ++covered_data;
                    }
                    if(covered_data >= buf[PAGE_HEAD_DATA_NUM])
                        break;
                }
                if(covered_data >= buf[PAGE_HEAD_DATA_NUM])
                    break;
            }
            table_page = buf[PAGE_HEAD_NEXT_PAGE];
        }
    }
}

void DataManager::getTableInfo(std::string table, Data& data, int& table_page, int& has_index){
    if(table == SYS_OBJ){
        table_page = FIRST_SYSOBJ_PAGE;
		has_index = 0;
        data.setLengthAsSysObj();
    }
    else if(table == SYS_COLUMN){
        table_page = FIRST_SYSCOLUMN_PAGE;
		has_index = 0;
        data.setLengthAsSysColumn();
    }
    else if(table == SYS_INDEX){
        table_page = FIRST_SYSINDEX_PAGE;
		has_index = 0;
        data.setLengthAsSysIndex();
    }
    else{
        vector<search_condition> condition;
        condition.push_back(search_condition(N_TABLE_NAME,table));
        search_result res;
        searchData(condition, SYS_OBJ, res);
        if(res.size() == 0){
            cout << "Error : Table not found!" << endl;
            return;
        }
        string RID = res[0][N_RID];
        condition.clear();
        condition.push_back(search_condition(N_INDEX_ID,RID));
        searchData(condition, SYS_INDEX, res);
        if(res.size() == 0){
            cout << "Error : Table in system is ruined." << endl;
            return;
        }
        table_page = Data::intUnzip(&(res[0][N_TABLE_INDEX][0]),MAX_TABLE_INDEX);
        has_index = Data::intUnzip(&(res[0][N_TABLE_HAS_INDEX][0]),MAX_TABLE_HAS_INDEX);
        condition.clear();
        condition.push_back(search_condition(N_INDEX_ID,RID));
        searchData(condition, SYS_COLUMN, res);
        vector<int> lengthList;
        int hash[MAX_COL_NUM];
        for(int i = 0; i < res.size(); ++i)
            hash[Data::intUnzip(&(res[i][N_COLUMN_RANK][0]),MAX_COLUMN_RANK)] = i;
        for(int i = 0; i < res.size(); ++i)
            lengthList.push_back(Data::intUnzip(&(res[i][N_COLUMN_LENGTH][0]), MAX_COLUMN_LENGTH));
        data.setLength(lengthList);
    }
}

void DataManager::addIndex(std::string table, int column){
    int index;
    vector<search_condition> condition;
    condition.push_back(search_condition(N_TABLE_NAME,table));
    search_result res;
    searchData(condition, SYS_OBJ, res);
    if(res.size() == 0){
        cout << "Error : Table not found!" << endl;
        return;
    }
    string RID = res[0][N_RID];
    condition.clear();
    condition.push_back(search_condition(N_INDEX_ID,RID));
    searchData(condition, SYS_INDEX, res);
    if(res.size() == 0){
        cout << "Error : Table in system is ruined." << endl;
        return;
    }
    int has_index = Data::intUnzip(&(res[0][N_TABLE_HAS_INDEX][0]),MAX_TABLE_HAS_INDEX);
    if((has_index>>column)&1){
        cout << "Error : Index has already exist." << endl;
        return;
    }
    has_index |= (1 << column);
	vector<replace_pair> replace;
	replace.push_back(replace_pair(N_TABLE_HAS_INDEX, Data::intZip(has_index, MAX_TABLE_HAS_INDEX)));
	updateData(condition, SYS_INDEX, replace);
    int first_page = Data::intUnzip(&(res[0][N_TABLE_INDEX][0]),MAX_TABLE_INDEX);
    BufType buf = bpm->getPage(fileID, FIRST_DATABASE_PAGE, index);
    bpm->markDirty(index);
    int page_id = buf[DATABASE_PAGE_NEXT_EMPTY_PAGE];
    ++buf[DATABASE_PAGE_NEXT_EMPTY_PAGE];
    buf = bpm->getPage(fileID, page_id, index);
    bpm->markDirty(index);
    buf[2] = 1 << 30;
    buf = bpm->getPage(fileID, first_page + 1, index);
    bpm->markDirty(index);
    buf[INDEX_PAGE_POS + column * 2] = page_id;
    buf[INDEX_PAGE_POS + column * 2 + 1] = 0;
    buf[INDEX_PAGE_FIRST_PAGE + column] = page_id;
    int table_page = first_page;
    std::vector<std::string> singleData;
    Data readFunc;
    getTableInfo(table, readFunc, table_page, has_index);
    while(table_page != 1 << 30){
        int offset = 0, len;
        BufType buf, tmpbuf;
        buf = bpm->getPage(fileID, table_page, index);
        char* readbuf = (char*) (buf + PAGE_HEAD_LEN);
        int covered_data = 0;
        for(int i = PAGE_HEAD_IN_USE; i < PAGE_HEAD_LEN; ++i){
            for(int j = 0; j < 32; ++j){
                if(((buf[i]>>j)&1) == 1){
                    singleData.clear();
                    offset = (i-PAGE_HEAD_IN_USE) * 32 + j;
                    int nullInf;
                    readFunc.readData(readbuf + offset*buf[PAGE_HEAD_DATA_LEN], singleData, len, nullInf);
                    int hash = IndexManager::indexHash(singleData[column]);
                    tmpbuf = bpm->getPage(fileID, first_page + 1, index);
                    IndexManager im;
                    im.setDataManager(this);
                    im.setCurrentPosition(position(tmpbuf[INDEX_PAGE_POS + column*2], tmpbuf[INDEX_PAGE_POS + column*2 + 1]));
                    im.addNode(hash, table_page * MAX_DATA_NUM + offset, tmpbuf[INDEX_PAGE_FIRST_PAGE+column]);
                    tmpbuf[INDEX_PAGE_POS + column*2] = im.getCurrentPosition().first;
                    tmpbuf[INDEX_PAGE_POS + column*2 + 1] = im.getCurrentPosition().second;
                    bpm->markDirty(index);
                    ++covered_data;
                }
                if(covered_data >= buf[PAGE_HEAD_DATA_NUM])
                    break;
            }
            if(covered_data >= buf[PAGE_HEAD_DATA_NUM])
                break;
        }
        table_page = buf[PAGE_HEAD_NEXT_PAGE];
    }
}

void DataManager::removeIndex(std::string table, int column){
    int index;
    vector<search_condition> condition;
    condition.push_back(search_condition(N_TABLE_NAME,table));
    search_result res;
    searchData(condition, SYS_OBJ, res);
    if(res.size() == 0){
        cout << "Error : Table not found!" << endl;
        return;
    }
    string RID = res[0][N_RID];
    condition.clear();
    condition.push_back(search_condition(N_INDEX_ID,RID));
    searchData(condition, SYS_INDEX, res);
    if(res.size() == 0){
        cout << "Error : Table in system is ruined." << endl;
        return;
    }
    int has_index = Data::intUnzip(&(res[0][N_TABLE_HAS_INDEX][0]),MAX_TABLE_HAS_INDEX);
    if(!((has_index>>column)&1)){
        cout << "Error : No index for the column." << endl;
        return;
    }
    has_index ^= (1 << column);
    vector<replace_pair> replace;
	replace.push_back(replace_pair(N_TABLE_HAS_INDEX, Data::intZip(has_index, MAX_TABLE_HAS_INDEX)));
	updateData(condition, SYS_INDEX, replace);
}
